package sf.dsl.example;

import sf.core.DBField;
import sf.core.DBObject;
import sf.database.dialect.DBDialect;
import sf.spring.util.CollectionUtils;
import sf.tools.ArrayUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 通用的Example查询对象
 */
public class Example {

    protected boolean distinct;

    protected boolean count;

    protected boolean notNull;

    protected boolean forUpdate;
    /**
     * where 条件是否是使用了模板包装;针对无法确定是否是数据库关键字的情况.
     * @see sf.database.util.SimpleSQLTemplate
     */
    protected boolean whereWrapper;

    protected WithBuilder withBuilder;

    //查询字段
    protected List<SimpleField> selectColumns;

    protected SimpleField countColumn;

    /**
     * select字段权限最高,不为空,则不再判断selectColumns,excludeColumns,countColumn
     */
    protected String select;

    /**
     * 表信息,from
     */
    protected List<SimpleTable> fromTables = new ArrayList<>(4);
    /**
     * from条件信息
     */
    protected List<ICondition> fromConditions;
    /**
     * where条件信息
     */
    protected List<ICondition> whereConditions = new ArrayList<>();

    /**
     * 查询条件是否必须添加表别名,默认false
     */
    protected AtomicBoolean mustTableAlias = new AtomicBoolean(false);

    protected List<UnionBuilder> unions;


    protected List<SimpleField> groupByList;

    protected List<ICondition> havingList;

    protected WindowBuilder windowBuilder;
    /**
     * 排序字段
     */
    protected List<OrderField> orderByList;
    /**
     * 查询总数限制
     */
    protected OffsetLimit offsetLimit;

    protected DBDialect dialect;

    protected SQLFlag sqlFlag = new SQLFlag();

    /**
     * 默认exists为true
     */
    public Example(DBDialect dialect) {
        this.dialect = dialect;
    }

    public Example() {
    }

    /**
     * 适用于单表操作
     * @param entityClass
     */
    public Example(Class<? extends DBObject> entityClass) {
        from(entityClass);
    }

    public Example as(String alias) {
        addFromC(QPI.as(alias));
        return this;
    }

    /**
     * 可以设置子查询的别名
     * @param alias
     * @return
     */
    public Example whereTableAs(String alias) {
        whereConditions.add(QPI.as(alias));
        return this;
    }

    public Example addFlag(SQLFlag.Position position, String flag) {
        this.sqlFlag.addFlag(position, flag);
        return this;
    }

    /**
     * @param alias
     * @param columns  列名称列表,可以为null
     * @param subQuery
     * @return
     */
    public Example with(String alias, SimpleField[] columns, Example subQuery) {
        if (withBuilder == null) {
            withBuilder = new WithBuilder();
        }
        withBuilder.add(new WithBuilder.WithOne(alias, columns, subQuery));
        return this;
    }


    /**
     * 迭代
     * @param alias
     * @param simpleFields 列名称列表,可以为null
     * @param subQuery
     * @return
     */
    public Example withRecursive(String alias, SimpleField[] simpleFields, Example subQuery) {
        if (withBuilder == null) {
            withBuilder = new WithBuilder();
        }
        withBuilder.recursive(true).add(new WithBuilder.WithOne(alias, simpleFields, subQuery));
        return this;
    }

    /**
     * 定义在
     * @param alias
     * @param windowFunction
     * @return
     */
    public Example window(String alias, WindowFunction windowFunction) {
        if (windowBuilder == null) {
            windowBuilder = new WindowBuilder();
        }
        windowBuilder.add(new WindowBuilder.WindowOne(alias, windowFunction));
        return this;
    }

    public Example select(String select) {
        this.select = select;
        return this;
    }

    /**
     * 指定要查询的属性列 - 这里会自动映射到表字段
     * @param properties
     * @return
     */
    public Example select(SimpleField... properties) {
        if (CollectionUtils.isNotEmpty(this.selectColumns)) {
            return this;
        }
        if (properties != null && properties.length > 0) {
            if (this.selectColumns == null) {
                this.selectColumns = new ArrayList<>(8);
            }
            for (int i = 0; i < properties.length; i++) {
                SimpleField property = properties[i];
                this.selectColumns.add(property);
            }
        }
        return this;
    }

    public Example selectProperties(DBField... properties) {
        if (ArrayUtils.isNotEmpty(properties)) {
            SimpleField[] array = new SimpleField[properties.length];
            for (int i = 0; i < properties.length; i++) {
                array[i] = DSLMethod.simpleField(fromTables, properties[i]);
            }
            return select(array);
        }
        return this;
    }

    public Example select(Collection<SimpleField> properties) {
        if (CollectionUtils.isNotEmpty(this.selectColumns)) {
            return this;
        }
        if (properties != null && !properties.isEmpty()) {
            if (this.selectColumns == null) {
                this.selectColumns = new ArrayList<>(8);
            }
            for (SimpleField property : properties) {
                this.selectColumns.add(property);
            }
        }
        return this;
    }

    public Example selectDistinct(SimpleField... properties) {
        if (properties != null && properties.length > 0) {
            if (this.selectColumns == null) {
                this.selectColumns = new ArrayList<>(8);
            }
            for (int i = 0; i < properties.length; i++) {
                SimpleField property = properties[i];
                this.selectColumns.add(property);
            }
            this.distinct = true;
        }
        return this;
    }

    public Example selectDistinct(DBField... properties) {
        if (properties != null && properties.length > 0) {
            if (this.selectColumns == null) {
                this.selectColumns = new ArrayList<>(8);
            }
            for (int i = 0; i < properties.length; i++) {
                SimpleField property = DSLMethod.simpleField(fromTables, properties[i]);
                this.selectColumns.add(property);
            }
            this.distinct = true;
        }
        return this;
    }

    public Example selectDistinct(Collection<SimpleField> properties) {
        if (properties != null && !properties.isEmpty()) {
            if (this.selectColumns == null) {
                this.selectColumns = new ArrayList<>(8);
            }
            this.selectColumns.addAll(properties);
            this.distinct = true;
        }
        return this;
    }

    public Example selectCount(SimpleField property) {
        this.countColumn = property;
        this.count = true;
        return this;
    }

    public Example selectCount(DBField property) {
        this.countColumn = SimpleField.create(property);
        this.count = true;
        return this;
    }

    public Example selectCount() {
        this.count = true;
        return this;
    }

    public Example from(SimpleTable... tables) {
        Collections.addAll(fromTables, tables);
        return this;
    }

    /**
     * 适用于单表操作
     * @param entityClass
     * @return
     */
    protected Example from(Class<? extends DBObject> entityClass) {
        from(new SimpleTable(entityClass));
        hasMustTableAlias();
        return this;
    }

    protected Example from() {
        return this;
    }

    /**
     * from中的条件,内部访问
     * @param conditions
     * @return
     */
    protected Example addFromC(ICondition... conditions) {
        if (fromConditions == null) {
            this.fromConditions = new LinkedList<>();
        }
        if (ArrayUtils.isNotEmpty(conditions)) {
            Collections.addAll(fromConditions, conditions);
        }
        return this;
    }

    protected Example addFromC(Collection<ICondition> conditions) {
        if (fromConditions == null) {
            this.fromConditions = new LinkedList<>();
        }
        if (CollectionUtils.isNotEmpty(conditions)) {
            fromConditions.addAll(conditions);
        }
        return this;
    }

    /**
     * 简写,添加from中的条件
     * @param conditions
     * @return
     */
    public Example f(ICondition... conditions) {
        return addFromC(conditions);
    }

    public Example f(Collection<ICondition> conditions) {
        return addFromC(conditions);
    }

    public Example where() {
        //什么都不做
        return this;
    }

    public Example where(ICondition... conditions) {
        return addWhereC(conditions);
    }

    /**
     * 1=1
     * @return
     */
    public Example trueCondition() {
        whereConditions.add(QPI.trueCondition());
        return this;
    }

    /**
     * 1=0
     * @return
     */
    public Example falseCondition() {
        whereConditions.add(QPI.falseCondition());
        return this;
    }

    public Example and() {
        whereConditions.add(QPI.and());
        return this;
    }

    public Example or() {
        whereConditions.add(QPI.or());
        return this;
    }

    public Example leftP() {
        whereConditions.add(QPI.leftP());
        return this;
    }

    public Example rightP() {
        whereConditions.add(QPI.rightP());
        return this;
    }

    /**
     * where中的条件,内部访问
     * @param conditions
     * @return
     */
    protected Example addWhereC(ICondition... conditions) {
        if (ArrayUtils.isNotEmpty(conditions)) {
            Collections.addAll(whereConditions, conditions);
        }
        return this;
    }

    protected Example addWhereC(Collection<ICondition> conditions) {
        if (CollectionUtils.isNotEmpty(conditions)) {
            whereConditions.addAll(conditions);
        }
        return this;
    }

    /**
     * 简写,添加where中的条件
     * @param conditions
     * @return
     */
    public Example w(ICondition... conditions) {
        return addWhereC(conditions);
    }

    public Example w(Collection<ICondition> conditions) {
        return addWhereC(conditions);
    }

    /**
     * @param simpleTable
     * @param conditions
     * @return
     */
    public Example leftJoin(SimpleTable simpleTable, ICondition... conditions) {
        addFromC(QPI.leftJoin(), QPI.tableInfo(simpleTable));
        if (ArrayUtils.isNotEmpty(conditions)) {
            addFromC(conditions);
        }
        return this;
    }

    /**
     * @param simpleTable
     * @param conditions
     * @return
     */
    public Example rightJoin(SimpleTable simpleTable, ICondition... conditions) {
        addFromC(QPI.rightJoin(), QPI.tableInfo(simpleTable));
        if (ArrayUtils.isNotEmpty(conditions)) {
            addFromC(conditions);
        }
        return this;
    }

    /**
     * @param simpleTable
     * @param conditions
     * @return
     */
    public Example innerJoin(SimpleTable simpleTable, ICondition... conditions) {
        addFromC(QPI.innerJoin(), QPI.tableInfo(simpleTable));
        if (ArrayUtils.isNotEmpty(conditions)) {
            addFromC(conditions);
        }
        return this;
    }

    /**
     * @param simpleTable
     * @param conditions
     * @return
     */
    public Example fullJoin(SimpleTable simpleTable, ICondition... conditions) {
        addFromC(QPI.fullJoin(), QPI.tableInfo(simpleTable));
        if (ArrayUtils.isNotEmpty(conditions)) {
            addFromC(conditions);
        }
        return this;
    }

    /**
     * @param simpleTable
     * @param conditions
     * @return
     */
    public Example crossJoin(SimpleTable simpleTable, ICondition... conditions) {
        addFromC(QPI.crossJoin(), QPI.tableInfo(simpleTable));
        if (ArrayUtils.isNotEmpty(conditions)) {
            addFromC(conditions);
        }
        return this;
    }

    public Example exists(Example example) {
        whereConditions.add(QPI.exists(example));
        return this;
    }

    public Example notExists(Example example) {
        whereConditions.add(QPI.notExists(example));
        return this;
    }

    public Example add(Example example) {
        whereConditions.add(QPI.addCondition(" ("));
        whereConditions.add(QPI.add(example));
        whereConditions.add(QPI.addCondition(") "));
        return this;
    }

    public Example union(Example unionQuery) {
        if (unions == null) {
            unions = new ArrayList<>();
        }
        unions.add(UnionBuilder.union(unionQuery));
        return this;
    }

    public Example unionAll(Example unionQuery) {
        if (unions == null) {
            unions = new ArrayList<>();
        }
        unions.add(UnionBuilder.unionAll(unionQuery));
        return this;
    }

    /**
     * 分页参数
     * @param start
     * @param limit
     * @return
     */
    public Example offsetLimit(long start, int limit) {
        this.offsetLimit = new OffsetLimit(start, limit);
        return this;
    }

    public Example groupBy(SimpleField... properties) {
        if (groupByList == null) {
            this.groupByList = new LinkedList<>();
        }
        Collections.addAll(this.groupByList, properties);
        return this;
    }

    public Example groupBy(DBField... properties) {
        if (groupByList == null) {
            this.groupByList = new LinkedList<>();
        }
        for (DBField field : properties) {
            this.groupByList.add(SimpleField.create(field));
        }
        return this;
    }

    public Example having(ICondition... conditions) {
        if (havingList == null) {
            havingList = new ArrayList<>();
        }
        Collections.addAll(havingList, conditions);
        return this;
    }

    private void addOrderBy(SimpleField property, Order order, OrderField.OrderNull orderNull) {
        if (orderByList == null) {
            orderByList = new LinkedList<>();
        }
        this.orderByList.add(new OrderField(property, order, orderNull));
    }

    private void addOrderBy(DBField property, Order order, OrderField.OrderNull orderNull) {
        if (orderByList == null) {
            orderByList = new LinkedList<>();
        }
        this.orderByList.add(new OrderField(DSLMethod.simpleField(fromTables, property), order, orderNull));
    }

    public Example orderBy(SimpleField property, Order order, OrderField.OrderNull orderNull) {
        addOrderBy(property, order, orderNull);
        return this;
    }

    public Example orderBy(SimpleField property, Order order) {
        addOrderBy(property, order, null);
        return this;
    }

    public Example orderBy(SimpleField... properties) {
        return orderByAsc(properties);
    }

    public Example orderByAsc(SimpleField... properties) {
        for (SimpleField field : properties) {
            addOrderBy(field, Order.ASC, null);
        }
        return this;
    }

    public Example orderByDesc(SimpleField... properties) {
        for (SimpleField field : properties) {
            addOrderBy(field, Order.DESC, null);
        }
        return this;
    }

    public Example orderBy(DBField property, Order order) {
        addOrderBy(property, order, null);
        return this;
    }

    /**
     * @param property
     * @param order
     * @param orderNull nulls first或nulls last,可以为null
     * @return
     */
    public Example orderBy(DBField property, Order order, OrderField.OrderNull orderNull) {
        addOrderBy(property, order, orderNull);
        return this;
    }

    public Example orderBy(DBField... properties) {
        return orderByAsc(properties);
    }

    public Example orderByAsc(DBField... properties) {
        for (DBField field : properties) {
            addOrderBy(field, Order.ASC, null);
        }
        return this;
    }

    public Example orderByDesc(DBField... properties) {
        for (DBField field : properties) {
            addOrderBy(field, Order.DESC, null);
        }
        return this;
    }

    public Criteria createCriteria() {
        Criteria criteria = new Criteria();
        criteria.list = this.whereConditions;
        criteria.fromTables = this.fromTables;
        criteria.notNull = this.notNull;
        criteria.temp = this;
        return criteria;
    }

    public void clear() {
        fromTables.clear();
        if (fromConditions != null) {
            fromConditions.clear();
        }
        whereConditions.clear();
        if (orderByList != null) {
            orderByList.clear();
        }
        if (groupByList != null) {
            groupByList.clear();
        }
        distinct = false;
    }

    /**
     * 是否强行需要设置别名
     */
    protected void hasMustTableAlias() {
        if (fromTables.size() > 1) {
            mustTableAlias.set(true);
        }
    }

    public AtomicBoolean getMustTableAlias() {
        return mustTableAlias;
    }

    public SimpleField getCountColumn() {
        return countColumn;
    }

    public List<SimpleTable> getFromTables() {
        return fromTables;
    }

    public List<OrderField> getOrderByList() {
        return orderByList;
    }

    public List<SimpleField> getGroupByList() {
        return groupByList;
    }

    public List<ICondition> getHavingList() {
        return havingList;
    }

    public List<SimpleField> getSelectColumns() {
        return selectColumns;
    }

    public boolean isDistinct() {
        return distinct;
    }

    public void setDistinct(boolean distinct) {
        this.distinct = distinct;
    }

    public boolean isForUpdate() {
        return forUpdate;
    }

    public void setForUpdate(boolean forUpdate) {
        this.forUpdate = forUpdate;
    }

    public boolean isCount() {
        return count;
    }

    public void setCount(boolean count) {
        this.count = count;
    }

    public OffsetLimit getOffsetLimit() {
        return offsetLimit;
    }

    public void setOffsetLimit(OffsetLimit offsetLimit) {
        this.offsetLimit = offsetLimit;
    }

    public Example setDialect(DBDialect dialect) {
        this.dialect = dialect;
        return this;
    }

    public DBDialect getDialect() {
        return dialect;
    }

    public boolean isWhereWrapper() {
        return whereWrapper;
    }

    public void setWhereWrapper(boolean whereWrapper) {
        this.whereWrapper = whereWrapper;
    }
}